/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.midorinext.android.syncintegration

import android.os.SystemClock.sleep
import android.widget.EditText
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.Until
import okhttp3.mockwebserver.MockWebServer
import org.hamcrest.Matchers.allOf
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.midorinext.android.R
import org.midorinext.android.helpers.AndroidAssetDispatcher
import org.midorinext.android.helpers.HomeActivityTestRule
import org.midorinext.android.helpers.TestAssetHelper
import org.midorinext.android.helpers.ext.toUri
import org.midorinext.android.helpers.ext.waitNotNull
import org.midorinext.android.ui.robots.accountSettings
import org.midorinext.android.ui.robots.homeScreen
import org.midorinext.android.ui.robots.navigationToolbar
import org.midorinext.android.ui.robots.settingsSubMenuLoginsAndPassword

@Suppress("RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
class SyncIntegrationTest {
    private lateinit var mDevice: UiDevice
    private lateinit var mockWebServer: MockWebServer

    @get:Rule
    val activityTestRule = HomeActivityTestRule()

    @Before
    fun setUp() {
        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
        mockWebServer = MockWebServer().apply {
            dispatcher = AndroidAssetDispatcher()
            start()
        }
    }

    @After
    fun tearDown() {
        mockWebServer.shutdown()
    }

    // History item Desktop -> Midori
    @Test
    fun checkHistoryFromDesktopTest() {
        signInFxSync()
        tapReturnToPreviousApp()
        // Let's wait until homescreen is shown to go to three dot menu
        TestAssetHelper.waitingTime
        mDevice.waitNotNull(Until.findObjects(By.res("org.midorinext.android.debug:id/counter_root")))
        homeScreen {
        }.openThreeDotMenu {
        }.openHistory {
        }
        historyAfterSyncIsShown()
    }

    // Bookmark item Desktop -> Midori
    @Test
    fun checkBookmarkFromDesktopTest() {
        signInFxSync()
        tapReturnToPreviousApp()
        homeScreen {
        }.openThreeDotMenu {
        }.openBookmarks { }
        bookmarkAfterSyncIsShown()
    }

    @Test
    fun checkAccountSettings() {
        signInFxSync()
        mDevice.waitNotNull(Until.findObjects(By.text("Account")), TestAssetHelper.waitingTime)

        goToAccountSettings()
        // This function to be added to the robot once the status of checkboxes can be checked
        // currently is not possible to select each one (History/Bookmark) and verify its status
        // verifyCheckBoxesSelected()
        // Then select/unselect each one and verify again that its status is correct
        // See issue #6544
        accountSettings {
            verifyBookmarksCheckbox()
            verifyHistoryCheckbox()
            verifySignOutButton()
            verifyDeviceName()
        }.disconnectAccount {
            mDevice.waitNotNull(Until.findObjects(By.text("Settings")), TestAssetHelper.waitingTime)
            verifySettingsView()
        }
    }

    // Login item Desktop -> Midori
    @Test
    fun checkLoginsFromDesktopTest() {
        homeScreen {
        }.openThreeDotMenu {
        }.openSettings {
        }.openLoginsAndPasswordSubMenu {
        }.openSyncLogins {
            // Tap to sign in from Logins menu
            tapOnUseEmailToSignIn()
            typeEmail()
            tapOnContinueButton()
            typePassword()
            tapOnSignIn()
        }
        // Automatically goes back to Logins and passwords view
        settingsSubMenuLoginsAndPassword {
            verifyDefaultView()
            // Sync logings option is set to Off, no synced logins yet
            verifyDefaultViewBeforeSyncComplete()
        }.openSavedLogins {
            // Discard the secure your device message
            tapSetupLater()
            // Check the logins synced
            verifySavedLoginsAfterSync()
        }.goBack {
            // After checking the synced logins
            // on Logins and Passwords menu the Sync logins option is set to On
            verifyDefaultViewAfterSync()
        }
    }

    // Bookmark item Midori -> Desktop
    @Test
    fun checkBookmarkFromDeviceTest() {
        val defaultWebPage = "example.com".toUri()!!
        navigationToolbar {
        }.enterURLAndEnterToBrowser(defaultWebPage) {
        }.openThreeDotMenu {
        }.bookmarkPage {
        }.openThreeDotMenu {
        }.openSettings {
        }.openTurnOnSyncMenu {
            useEmailInsteadButton()
            typeEmail()
            tapOnContinueButton()
            typePassword()
            sleep(TestAssetHelper.waitingTimeShort)
            tapOnSignIn()
        }
    }

    // History item Midori -> Desktop
    @Test
    fun checkHistoryFromDeviceTest() {
        val defaultWebPage = "example.com".toUri()!!
        navigationToolbar {
        }.enterURLAndEnterToBrowser(defaultWebPage) {
        }.openThreeDotMenu {
        }.openSettings {
        }.openTurnOnSyncMenu {
            useEmailInsteadButton()
            typeEmail()
            tapOnContinueButton()
            typePassword()
            sleep(TestAssetHelper.waitingTimeShort)
            tapOnSignIn()
        }
    }

    // Useful functions for the tests
    fun typeEmail() {
        val emailInput = mDevice.findObject(
            UiSelector()
                .instance(0)
                .className(EditText::class.java)
        )
        emailInput.waitForExists(TestAssetHelper.waitingTime)

        val emailAddress = javaClass.classLoader!!.getResource("email.txt").readText()
        emailInput.setText(emailAddress)
    }

    fun tapOnContinueButton() {
        val continueButton = mDevice.findObject(By.res("submit-btn"))
        continueButton.clickAndWait(Until.newWindow(), TestAssetHelper.waitingTime)
    }

    fun typePassword() {
        val passwordInput = mDevice.findObject(
            UiSelector()
                .instance(0)
                .className(EditText::class.java)
        )

        val passwordValue = javaClass.classLoader!!.getResource("password.txt").readText()
        passwordInput.setText(passwordValue)
    }

    fun tapOnSignIn() {
        mDevice.waitNotNull(Until.findObjects(By.text("Sign in")))
        // Let's tap on enter, sometimes depending on the device the sign in button is
        // hidden by the keyboard
        mDevice.pressEnter()
    }

    fun historyAfterSyncIsShown() {
        mDevice.waitNotNull(Until.findObjects(By.text("http://www.example.com/")), TestAssetHelper.waitingTime)
    }

    fun bookmarkAfterSyncIsShown() {
        val bookmarkEntry = mDevice.findObject(By.text("Example Domain"))
        bookmarkEntry.isEnabled()
    }

    fun tapReturnToPreviousApp() {
        mDevice.waitNotNull(Until.findObjects(By.text("Save")), TestAssetHelper.waitingTime)
        mDevice.waitNotNull(Until.findObjects(By.text("Settings")), TestAssetHelper.waitingTime)

        /* Wait until the Settings shows the account synced */
        mDevice.waitNotNull(Until.findObjects(By.text("Account")), TestAssetHelper.waitingTime)
        mDevice.waitNotNull(Until.findObjects(By.res("org.midorinext.android.debug:id/email")), TestAssetHelper.waitingTime)
        TestAssetHelper.waitingTime
        // Go to Homescreen
        mDevice.pressBack()
    }

    fun signInFxSync() {
        homeScreen {
        }.openThreeDotMenu {
            verifySettingsButton()
        }.openSettings {}
        settingsAccount()
        useEmailInsteadButton()

        typeEmail()
        tapOnContinueButton()
        typePassword()
        sleep(TestAssetHelper.waitingTimeShort)
        tapOnSignIn()
    }

    fun goToAccountSettings() {
        enterAccountSettings()
        mDevice.waitNotNull(Until.findObjects(By.text("Device name")), TestAssetHelper.waitingTime)
    }
}

fun settingsAccount() = onView(allOf(withText("Turn on Sync"))).perform(click())
fun useEmailInsteadButton() = onView(withId(R.id.signInEmailButton)).perform(click())
fun enterAccountSettings() = onView(withId(R.id.email)).perform(click())
